package org.atlantis.grosbeak.castor;

import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.atlantis.grosbeak.lang.Exceptions;
import org.atlantis.grosbeak.lang.Mirror;
import org.atlantis.grosbeak.lang.Resources;
import org.atlantis.grosbeak.lang.codec.MD5Generator;

/**
 * 转换器的工厂类。使用方法如：
 * 
 * <pre>
 * int i = Castors.me().cast(&quot;123&quot;, Integer.class);
 * boolean flag = Castors.me().cast(&quot;yes&quot;, Integer.class);
 * String[] strs = Castors.me().cast(&quot;SNE,Atlantis,Lucifer&quot;, String[].class);
 * </pre>
 * 
 * @author <a href="mailto:seenoevil.cn@gmail.com">SeeNoEvil</a>
 * 
 */
public class Castors {

	private static Castors castors = new Castors();

	/**
	 * 得到一个Castors的单例。
	 * 
	 * @return Castors的单例。
	 */
	public static Castors me() {
		return castors;
	}

	/**
	 * 得到一个新的Castors的实例。调用create()方法得到的是不同的实例。
	 * 
	 * @return Castors实例。
	 */
	public static Castors create() {
		return new Castors();
	}

	/**
	 * 是否能从from直接转换到to而不出现异常。
	 * 
	 * @param from
	 *            需要转换的类型。
	 * @param to
	 *            转换到的类型。
	 * @return 是否能直接转换。
	 */
	public static boolean canCastDirectly(Class<?> from, Class<?> to) {
		// 如果to为null，那么就相当于把from转换为Object。
		if (null == to || null == from || from == to)
			return true;

		if (to.isAssignableFrom(from))
			return true;

		Mirror<?> fromM = Mirror.me(from);
		Mirror<?> toM = Mirror.me(to);

		if (from.isPrimitive() && to.isPrimitive())
			if (fromM.isPrimitiveNumber() && toM.isPrimitiveNumber())
				return true;
		if (fromM.getWrapperClass() == toM.getWrapperClass())
			return true;

		return false;
	}

	/**
	 * 是否能从from转换到to（只要能转换就行，不管采用什么样的方式，直接转换也好，使用转换器转换也罢）。
	 * 
	 * @param from
	 *            需要转换的类型。
	 * @param to
	 *            转换到的类型。
	 * @return 是否能转换。
	 */
	public boolean canCast(Class<?> from, Class<?> to) {
		if (Castors.canCastDirectly(from, to))
			return true;
		return canCastWithoutDirectly(from, to);
	}

	/**
	 * 是否能使用转换器从from转换到to。
	 * 
	 * @param from
	 *            需要转换的类型。
	 * @param to
	 *            转换到的类型。
	 * @return 是否能转换。
	 */
	public boolean canCastWithoutDirectly(Class<?> from, Class<?> to) {
		if (from.isPrimitive())
			from = Mirror.me(from).getWrapperClass();
		if (to.isPrimitive())
			to = Mirror.me(to).getWrapperClass();

		Class<?> fromMirAd = Mirror.me(from).getAbstractDesc();
		Class<?> toMirAd = Mirror.me(to).getAbstractDesc();

		String keyA = reckonKey(from, to);
		String keyB = reckonKey(fromMirAd, toMirAd);
		return storage.containsKey(keyA) || storage.containsKey(keyB);
	}

	private Set<String> paths;
	private Map<String, Castor<?, ?>> storage;

	private Castors() {
		resetPaths();
	}

	/**
	 * 重新设置转换器寻找路径。
	 * 
	 * @return Castors对象。
	 */
	public synchronized Castors resetPaths() {
		paths = new HashSet<String>();
		paths.add("org.atlantis.grosbeak.castor.defaults");
		reload();
		return this;
	}

	/**
	 * 添加转换器的寻找路径。
	 * 
	 * @param paths
	 *            转换器的寻找路径数组（路径如：org.atlanti.grosbeak.lang.castor.custom）。
	 * @return Castors对象。
	 */
	public synchronized Castors addPath(String... paths) {
		int orginLen = this.paths.size();
		if (null != paths) {
			for (String path : paths) {
				if (null != path)
					this.paths.add(path);
			}

			// 如果确实添加进去了，那么就reload()，否则不reload（节省性能）。
			if (this.paths.size() > orginLen)
				reload();
		}
		return this;
	}

	/**
	 * 完全使用自定义的转换器，不使用框架默认提供的。
	 * 
	 * @param paths
	 *            转换器的寻找路径数组（路径如：org.atlantis.grosbeak.lang.castor.custom）。
	 * @return Castors对象。
	 */
	public synchronized Castors customPath(String... paths) {
		this.paths = new HashSet<String>();
		for (String path : paths) {
			if (null != path)
				this.paths.add(path);
		}
		reload();
		return this;
	}

	/**
	 * 开始进行类型转换。
	 * 
	 * @param <FROM>
	 *            需要转换的类型。
	 * @param <TO>
	 *            转换到的类型。
	 * @param src
	 *            需要转换的对象。
	 * @param from
	 *            需要转换的类型。为什么有src还需要from，可以直接通过src.getClass()得到from呀！
	 *            但是例如如果是想在String和List之间转换 。转换器是：<br>
	 * 
	 *            <pre>
	 *            public class String2List extends AbstraceCastor&lt;String.class, List.class&gt;{...}
	 * </pre>
	 * 
	 *            那么如果不填入from直接从src来获取需要转换的类型：Castors.me().cast(new ArrayList(),
	 *            String.class);
	 *            通过getFrom()方法得到的就是ArrayList.class。而不同转换器之间的识别标志是通过getFrom
	 *            ()得到的类型的hashcode，
	 *            和getTo()得到的类型的hashcode计算得来的。所以上面计算到的是String和ArrayList的转换器 ，
	 *            但没有这个转换器，那么就会抛出 找不到转换器的异常。<br>
	 *            <font
	 *            color="#FF0000">因此要注意from参数和to参数要和相应转换器中的getFrom()返回值和getTo
	 *            ()返回值保持一致。</font>
	 * 
	 * @param to
	 *            转换到的类型。
	 * @return 转换完成后的对象。
	 * @throws FailToCastException
	 *             转换失败时抛出。
	 */
	@SuppressWarnings("unchecked")
	public <FROM, TO> TO cast(Object src, Class<FROM> from, Class<TO> to)
			throws FailToCastException {
		// 首先处理src为空的情况
		if (null == src) {
			return null;
		}

		// 然后看看能不能直接强制转换
		if (Castors.canCastDirectly(from, to))
			return (TO) src;

		// 如果不能，就需要在转换器仓库中查找相应的转换器了。

		// 如果to是基本类型，那么使用该基本类型的包装类
		Class _to = to;
		if (to.isPrimitive())
			_to = Mirror.me(to).getWrapperClass();

		// 如果from是基本类型，那么使用该基本类型的包装类
		Class _from = from;
		if (from.isPrimitive())
			_from = Mirror.me(from).getWrapperClass();

		Class<?> _fromMirDa = Mirror.me(_from).getAbstractDesc();
		Class<?> _toMirDa = Mirror.me(_to).getAbstractDesc();

		String keyA = reckonKey(_from, _to);
		String keyB = reckonKey(_fromMirDa, _toMirDa);

		Castor castorA = storage.get(keyA);
		Castor castorB = storage.get(keyB);

		Castor castor = (castorA == null) ? castorB : castorA;
		if (null == castor)
			throw Exceptions.makeThrow(FailToCastException.class,
					"Can't find castor '%s' -> '%s'.", _from.getName(), _to
							.getName());
		Type fromClass = castor.getFrom();
		Type toClass = castor.getTo();
		try {
			if ((fromClass == _from && toClass == _to)
					|| (fromClass == _fromMirDa && toClass == _toMirDa))
				return (TO) castor.obverseCast(src, to);
			else
				// 这里不需要判断fromClass == to && toClass ==
				// from.getClass()，因为除了A转B，就是B转A，如果还有其他情况就只能抛出异常了。
				return (TO) castor.reverseCast(src, to);
		} catch (Exception e) {
			throw Exceptions.makeThrow(FailToCastException.class, e,
					"Can't cast from [%s] to [%s], because:\n%s:%s", src
							.getClass().getName(), _to.getName(), e.getClass()
							.getSimpleName(), e.getMessage());
		}
	}

	/**
	 * 该方法是调用cast(Object src, Class&lt;FROM&gt; from, Class&lt;TO&gt;
	 * to)实现，from参数是该方法通过src.getClass()自动传入的。 所以转换器容器中必须存在src.getClass()类型的转换器。
	 * 
	 * @param <TO>
	 *            转换到的类型。
	 * @param src
	 *            需要转换的对象。
	 * @param to
	 *            转换到的类型。
	 * @return 转换后的对象。
	 * 
	 * @throws FailToCastException
	 *             转换失败时抛出。
	 * 
	 * @see #cast(Object, Class, Class)
	 */
	public <TO> TO castTo(Object src, Class<TO> to) throws FailToCastException {
		return cast(src, null == src ? null : src.getClass(), to);
	}

	/**
	 * 把指定对象转换成String类型。如果找不到指定转换器时则直接使用toString()方法返回。
	 * 
	 * @param src
	 *            需要转换的对象。
	 * @return String。
	 * 
	 * @see #castTo(Object, Class)
	 * @see #toString()
	 */
	public String castToString(Object src) {
		try {
			return castTo(src, String.class);
		} catch (FailToCastException e) {
			return src.toString();
		}
	}

	/* 加载所有的转换器，包括框架默认的和用户自定义的。 */
	private void reload() {
		storage = new HashMap<String, Castor<?, ?>>();
		for (String path : paths) {
			List<Class<?>> clazzs = Resources.findClass(path);
			for (Class<?> clazz : clazzs) {
				if (!Modifier.isAbstract(clazz.getModifiers())
						&& !Modifier.isStatic(clazz.getModifiers())
						&& Castor.class.isAssignableFrom(clazz)) {
					try {
						Castor<?, ?> castor = (Castor<?, ?>) clazz
								.newInstance();
						Type from = castor.getFrom();
						Type to = castor.getTo();
						String key = reckonKey(from, to);
						storage.put(key, castor);
					} catch (Exception e) {
						throw Exceptions.makeThrow(FailToCastException.class,
								e, "Fail to create castor '%s',because [%s].",
								clazz.getName(), e.getMessage());
					}
				}
			}
		}
	}

	/* 通过Class的hashCode计算出storage的key。 */
	private String reckonKey(Type from, Type to) {

		// 擦出泛型
		from = Mirror.getClassRawType(from);
		to = Mirror.getClassRawType(to);

		double fromHC = from.hashCode();
		double toHC = to.hashCode();
		double resultNum = Math.abs(fromHC - toHC) + (double) (fromHC / toHC)
				+ (double) (toHC / fromHC);
		String result = new MD5Generator()
				.getMD5Code(String.valueOf(resultNum));
		return result;
	}
}
